unit database_types;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  database_base, fpjson;

type

  { TDatabaseTypesFrame }

  TDatabaseTypesFrame = class(TDatabaseBaseFrame)
    CompactModeSelectorComboBox: TComboBox;
    EquipTypeNameLabel: TLabel;
    EquipTypesGroupBox: TGroupBox;
    EquipTypesListBox: TListBox;
    EquipTypesNameEdit: TEdit;
    SetMaxEquipTypesButton: TButton;
    SetMaxWeaponTypesButton: TButton;
    SetMaxArmourTypesButton: TButton;
    WeaponTypeNameLabel: TLabel;
    ArmourTypeNameLabel: TLabel;
    WeaponTypesGroupBox: TGroupBox;
    ArmourTypesGroupBox: TGroupBox;
    WeaponTypesListBox: TListBox;
    SkillTypesNameEdit: TEdit;
    SkillTypeNameLabel: TLabel;
    SkillTypesGroupBox: TGroupBox;
    SkillTypesListBox: TListBox;
    SetMaxElementsButton: TButton;
    ElementNameEdit: TEdit;
    ElementsGroupBox: TGroupBox;
    ElementsListBox: TListBox;
    ElementNameLabel: TLabel;
    SetMaxSkillTypesButton: TButton;
    ArmourTypesListBox: TListBox;
    WeaponTypesNameEdit: TEdit;
    ArmourTypesNameEdit: TEdit;

    procedure CompactModeSelectorComboBoxChange(Sender: TObject);
    procedure NameEditKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FrameResize(Sender: TObject);
    procedure ItemNameEditChange(Sender: TObject);
    procedure ItemsListBoxSelectionChange(Sender: TObject; User: boolean);
    procedure Init; override;
    procedure SetMaxItemsButtonClick(Sender: TObject);
  private
    function NeedsCompactMode: Boolean;
    procedure FillCompactModeSelector;
    procedure FillListBox(ListBox: TListBox);
    function GetItemNames(ATag: Integer): TJSONArray;
    function FormatTypeName(Id: Integer; TypeName: String): String;
    procedure LayoutForWideMode;
    procedure LayoutForCompactMode;
  public

  end;

resourcestring
  TypesHeader = 'Types';

const
  JsonKeys: array [0..4] of String = (
    'elements', 'skillTypes', 'weaponTypes', 'armorTypes', 'equipTypes'
  );

implementation

{$R *.lfm}

uses
  constants, setmaximumformunit, LCLType;


{ TDatabaseTypesFrame }

procedure TDatabaseTypesFrame.Init;
var
  I, J: Integer;
  GroupBox: TGroupBox;
  ListBox: TListBox;
begin
  for I := 0 to ControlCount - 1 do
    if Controls[I] is TGroupBox then begin
      GroupBox := Controls[I] as TGroupBox;
      for J := 0 to GroupBox.ControlCount - 1 do
        if GroupBox.Controls[J] is TListBox then begin
          ListBox := GroupBox.Controls[J] as TListBox;
          FillListBox(ListBox);
          if ListBox.Items.Count > 0 then
            ListBox.ItemIndex := 0;
        end;
    end;

  FillCompactModeSelector;
  CompactModeSelectorComboBox.ItemIndex := 0;

  FrameResize(Self);
end;

procedure TDatabaseTypesFrame.SetMaxItemsButtonClick(Sender: TObject);
var
  ParentGroupBox: TGroupBox;
  ItemNames: TJSONArray;
  ListBox: TListBox;
  NewMaximum: Integer;
  I: Integer;
begin
  ParentGroupBox := (Sender as TButton).Parent as TGroupBox;
  ItemNames := GetItemNames(ParentGroupBox.Tag);

  for I := 0 to ParentGroupBox.ControlCount -1 do
    if ParentGroupBox.Controls[I] is TListBox then
      ListBox := ParentGroupBox.Controls[I] as TListBox;

  NewMaximum := SetMaximumForm.RequestValue(ItemNames.Count - 1, 99);
  while (NewMaximum > ItemNames.Count -1) do
    ItemNames.Add('');
  while (NewMaximum < ItemNames.Count -1) do
    ItemNames.Delete(ItemNames.Count - 1);

  FillListBox(ListBox);
end;

function TDatabaseTypesFrame.NeedsCompactMode: Boolean;
begin
  NeedsCompactMode := ClientWidth < Scale96ToForm(600);
end;

procedure TDatabaseTypesFrame.FillCompactModeSelector;
var
  I: Integer;
  GroupBox: TGroupBox;
begin
  with CompactModeSelectorComboBox.Items do begin
    Clear;
    BeginUpdate;

    for I := 0 to ControlCount -1 do
      if Controls[I] is TGroupBox then begin
        GroupBox := Controls[I] as TGroupBox;
        while GroupBox.Tag >= Count do
          Add('');

        CompactModeSelectorComboBox.Items[GroupBox.Tag] := GroupBox.Caption;
      end;

    EndUpdate;
  end;
end;

procedure TDatabaseTypesFrame.ItemsListBoxSelectionChange(Sender: TObject;
  User: boolean);
var
  ParentGroupBox: TGroupBox;
  ItemNames: TJSONArray;
  NameEdit: TEdit;
  I: Integer;
begin
  ParentGroupBox := (Sender as TListBox).Parent as TGroupBox;
  ItemNames := GetItemNames(ParentGroupBox.Tag);

  for I := 0 to ParentGroupBox.ControlCount -1 do
    if ParentGroupBox.Controls[I] is TEdit then
      NameEdit := ParentGroupBox.Controls[I] as TEdit;

  NameEdit.Enabled := (Sender as TListBox).ItemIndex >= 0;

  if (Sender as TListBox).ItemIndex < 0 then
    Exit;

  NameEdit.Text := ItemNames.Strings[(Sender as TListBox).ItemIndex + 1]
end;

procedure TDatabaseTypesFrame.ItemNameEditChange(Sender: TObject);
var
  ParentGroupBox: TGroupBox;
  ItemNames: TJSONArray;
  ListBox: TListBox;
  I: Integer;
begin
  ParentGroupBox := (Sender as TEdit).Parent as TGroupBox;
  ItemNames := GetItemNames(ParentGroupBox.Tag);

  for I := 0 to ParentGroupBox.ControlCount -1 do
    if ParentGroupBox.Controls[I] is TListBox then
      ListBox := ParentGroupBox.Controls[I] as TListBox;

  if ListBox.ItemIndex < 0 then
    Exit;

  ItemNames.Strings[ListBox.ItemIndex + 1] := (Sender as TEdit).Text;
  ListBox.Items[ListBox.ItemIndex] := FormatTypeName(
    ListBox.ItemIndex + 1, (Sender as TEdit).Text
  );
end;

procedure TDatabaseTypesFrame.FrameResize(Sender: TObject);
begin
  if NeedsCompactMode then
    LayoutForCompactMode
  else
    LayoutForWideMode;
end;

procedure TDatabaseTypesFrame.CompactModeSelectorComboBoxChange(Sender: TObject
  );
var
  I: Integer;
  GroupBox: TGroupBox;
begin
  if NeedsCompactMode then begin
    for I := 0 to ControlCount -1 do
      if Controls[I] is TGroupBox then begin
        GroupBox := Controls[I] as TGroupBox;
        GroupBox.Visible := GroupBox.Tag = (Sender as TComboBox).ItemIndex;
      end;
  end;
end;

procedure TDatabaseTypesFrame.NameEditKeyUp(Sender: TObject;
  var Key: Word; Shift: TShiftState);
var
  I: Integer;
  GroupBox: TGroupBox;
  ListBox: TListBox;
begin
  // TODO: check if this doesn't interfere with any OS-default actions in any OS
  // TODO: maybe check for (ssCtrl in Shift) if there's any possible problem
  if (Key = VK_UP) or (Key = VK_DOWN) then begin
    GroupBox := (Sender as TEdit).Parent as TGroupBox;
    for I := 0 to GroupBox.ControlCount -1 do
      if GroupBox.Controls[I] is TListBox then begin
        ListBox := GroupBox.Controls[I] as TListBox;
        Break
      end;

    if (Key = VK_UP) and (ListBox.ItemIndex > 0) then
      ListBox.ItemIndex := ListBox.ItemIndex - 1;

    if (Key = VK_DOWN) and (ListBox.ItemIndex < ListBox.Items.Count -1) then
      ListBox.ItemIndex := ListBox.ItemIndex + 1
  end;
end;

procedure TDatabaseTypesFrame.FillListBox(ListBox: TListBox);
var
  ParentGroupBox: TGroupBox;
  ItemNames: TJSONArray;
  I: Integer;
begin
  ParentGroupBox := ListBox.Parent as TGroupBox;
  ItemNames := GetItemNames(ParentGroupBox.Tag);

  with ListBox.Items do begin
    BeginUpdate;
    Clear;

    for I := 1 to ItemNames.Count -1 do
      Add(FormatTypeName(I, ItemNames.Strings[I]));

    EndUpdate;
  end;

end;

function TDatabaseTypesFrame.GetItemNames(ATag: Integer): TJSONArray;
var
  JsonKey: String;
begin
  JsonKey := JsonKeys[ATag];
  GetItemNames := Db.System.Arrays[JsonKey];
end;

function TDatabaseTypesFrame.FormatTypeName(Id: Integer; TypeName: String
  ): String;
begin
  FormatTypeName := rsTypeName.Format([Id, TypeName]);
end;

procedure TDatabaseTypesFrame.LayoutForWideMode;
var
  Margin: Integer = 8;
  NumGroupBoxes: Integer = 5;

  OuterWidth, InnerWidth: Integer;
  I: Integer;
  GroupBox: TGroupBox;
begin
  CompactModeSelectorComboBox.Visible := False;

  OuterWidth := (ClientWidth - Margin) div NumGroupBoxes;
  InnerWidth := OuterWidth - Margin;
  for I := 0 to ControlCount -1 do
    if Controls[I] is TGroupBox then begin
      GroupBox := Controls[I] as TGroupBox;

      GroupBox.Top := Margin;
      GroupBox.Height := ClientHeight - Margin * 2;

      GroupBox.Left := Margin + GroupBox.Tag * OuterWidth;
      GroupBox.Width := InnerWidth;

      GroupBox.Visible := True;
    end;
end;

procedure TDatabaseTypesFrame.LayoutForCompactMode;
var
  Margin: Integer = 8;
  I: Integer;
  GroupBox: TGroupBox;
begin
  CompactModeSelectorComboBox.Visible := True;

  for I := 0 to ControlCount -1 do
    if Controls[I] is TGroupBox then begin
      GroupBox := Controls[I] as TGroupBox;

      GroupBox.Top := CompactModeSelectorComboBox.Top
                   + CompactModeSelectorComboBox.Height + Margin;
      GroupBox.Height := ClientHeight - GroupBox.Top - Margin;

      GroupBox.Left := Margin;
      GroupBox.Width := ClientWidth - Margin * 2;

      GroupBox.Visible := CompactModeSelectorComboBox.ItemIndex = GroupBox.Tag;
    end;
end;


end.

